package com.um.springboot.starter.controller;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.um.springboot.starter.code.CommonCode;
import com.um.springboot.starter.entity.PresignedVO;
import com.um.springboot.starter.exception.ServiceException;
import com.um.springboot.starter.helper.SessionHelper;
import com.um.springboot.starter.orm.entity.Response;
import com.um.springboot.starter.properties.HshProperties;
import com.um.springboot.starter.utils.DateUtil;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.PostPolicy;
import io.minio.http.Method;
import io.swagger.annotations.Api;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.*;

import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author 甘天伟
 * @date 2025/3/20
 * 版权所有 广州优匠科技有限公司
 */
@Api(tags = "文件")
@RestController
@RequestMapping("/api/v1/minio")
@ConditionalOnProperty("hsh.sts.minio-enabled")
public class MinioController {

    @Autowired
    HshProperties hshProperties;

    @Autowired
    SessionHelper sessionHelper;

    @Autowired
    MinioClient minioClient;

    @SneakyThrows
    @PostMapping("/presigned_put")
    public Response<PresignedVO> presignedPut(@RequestBody Map<String, String> map) {
        String fileType = MapUtil.getStr(map, "fileType");
        String path = MapUtil.getStr(map, "path");
        PresignedVO vo = preCheck(fileType, path);

        String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.PUT)
                .bucket(hshProperties.getSts().getBucket())
                .object(vo.getObject())
                .expiry(hshProperties.getSts().getPresignedExpire(), TimeUnit.SECONDS)
                .build());

        vo.setUrl(url);
        return Response.success(vo);
    }

    @SneakyThrows
    @PostMapping("/presigned_post")
    public Response<PresignedVO> presignedPost(@RequestBody Map<String, String> map) {
        String fileType = MapUtil.getStr(map, "fileType");
        String path = MapUtil.getStr(map, "path");
        PresignedVO vo = preCheck(fileType, path);

        PostPolicy policy = new PostPolicy(hshProperties.getSts().getBucket(), ZonedDateTime.now().plusSeconds(hshProperties.getSts().getPresignedExpire()));
        policy.addContentLengthRangeCondition(0, hshProperties.getSts().getMaxSize());
        policy.addEqualsCondition("key", vo.getObject());
        Map<String, String> formData = minioClient.getPresignedPostFormData(policy);

        vo.setUrl(StrUtil.format("{}/{}", vo.getHost(), vo.getObject()));
        vo.setForm(formData);

        return Response.success(vo);
    }

    private PresignedVO preCheck(String fileType, String path) {
        if (StrUtil.isBlank(fileType)) {
            throw new ServiceException("文件类型不能为空！");
        }
        if (StrUtil.containsAny(path, "./", "//")) {
            throw new ServiceException("非法路径！");
        }
        if (!hshProperties.getFile().isAllowUpload()) {
            throw new ServiceException(CommonCode.NotOpen);
        }
        if (!hshProperties.getFile().isAllowAnonymousUpload() && StrUtil.isBlank(sessionHelper.getSessionUserId())) {
            throw new ServiceException(CommonCode.LoginExpired);
        }

        fileType = fileType.toLowerCase();
        List<String> allowFileTypes = StrUtil.split(hshProperties.getFile().getAllowFileType(), ',');
        if (!allowFileTypes.contains(fileType)) {
            throw new ServiceException("不允许上传的文件类型！");
        }

        // 添加日期路径
        String finalPath = DateUtil.getDateString(new Date(), "yyyy-MM");
        // 添加客户端自定义路径
        if (hshProperties.getSts().isAllowClientPath() && StrUtil.isNotBlank(path)) {
            finalPath = StrUtil.format("{}/{}", path, finalPath);
        }
        // 添加路径前缀
        if (StrUtil.isNotBlank(hshProperties.getSts().getPathPrefix())) {
            finalPath = StrUtil.format("{}/{}", hshProperties.getSts().getPathPrefix(), finalPath);
        }

        String objName = StrUtil.format("{}.{}", IdUtil.fastSimpleUUID().toUpperCase(), fileType);
        if (StrUtil.isNotBlank(finalPath)) {
            objName = StrUtil.format("{}/{}", finalPath, objName);
        }

        String host = hshProperties.getSts().getEndpoint();
        if (!StrUtil.contains(host, "://")) {
            host = StrUtil.format("https://{}", host);
        }
        if (StrUtil.isNotBlank(hshProperties.getSts().getRegion())) {
            // VirtualStyle: https://bucket.host
            host = host.replace("://", StrUtil.format("://{}.", hshProperties.getSts().getBucket()));
        } else {
            // PathStyle: https://host/bucket
            host = StrUtil.format("{}/{}", host, hshProperties.getSts().getBucket());
        }

        PresignedVO vo = new PresignedVO();
        vo.setHost(host);
        vo.setObject(objName);

        return vo;
    }

}
